#include "jz_lift.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
#include <QUuid>
#include <utility>

namespace application {
namespace lift {

JzLift::JzLift(int liftNum,
               const QString& baseUrl,
               const QString& liftCode,
               std::shared_ptr<kernel::ILogger> logger,
               QObject* parent)
    : BaseLift(liftNum, LiftType::JZ, logger, parent)
    , m_baseUrl(baseUrl)
    , m_liftCode(liftCode)
{
}

JzLift::~JzLift()
{
    if (m_networkManager) {
        m_networkManager->deleteLater();
        m_networkManager = nullptr;
    }
}

bool JzLift::initialize()
{
    if (!m_networkManager) {
        m_networkManager = new QNetworkAccessManager(this);
    }

    if (m_logger) {
        m_logger->info(QString("JZLift %1 initialized: %2")
            .arg(m_liftNum)
            .arg(m_baseUrl));
    }

    setStage(LiftStage::Done);
    setStatus(LiftStatus::Idle);
    return true;
}

bool JzLift::request(const LiftRequestInfo& request)
{
    setCurrentRequest(request);
    setStage(LiftStage::QueryStatus);
    setStatus(LiftStatus::Idle);
    return true;
}

bool JzLift::release()
{
    setStage(LiftStage::Done);
    setStatus(LiftStatus::Idle);
    setCurrentRequest(LiftRequestInfo{});
    m_statusCallback = nullptr;
    m_commandCallback = nullptr;
    return true;
}

void JzLift::requestLiftStatus(const QString& uuid, LiftResponseCallback callback)
{
    if (!m_networkManager) {
        if (m_logger) {
            m_logger->error("JZLift network manager not initialized");
        }
        if (callback) {
            callback();
        }
        return;
    }

    m_statusCallback = std::move(callback);
    m_dataId = uuid;
    m_currentDataId = uuid;

    QUrl url(buildUrl("/api/v1/elev/state"));
    QUrlQuery query;
    query.addQueryItem("code", m_liftCode);
    url.setQuery(query);

    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    QNetworkReply* reply = m_networkManager->get(request);
    connect(reply, &QNetworkReply::finished, this, &JzLift::onStatusReply);

    if (m_logger) {
        m_logger->debug(QString("JZLift %1: request status (%2)")
            .arg(m_liftNum)
            .arg(url.toString()));
    }
}

void JzLift::requestLiftCall(int liftNum, int callFloor, int aimFloor, LiftResponseCallback callback)
{
    Q_UNUSED(liftNum);

    QJsonObject payload;
    payload.insert("floor", callFloor);
    payload.insert("aim_floor", aimFloor);
    payload.insert("code", m_liftCode);

    sendPostRequest("/api/v1/elev/call", payload, callback, &JzLift::onCallReply);
}

void JzLift::requestLiftInLiftCloseDoor(int liftNum, LiftResponseCallback callback)
{
    Q_UNUSED(liftNum);
    requestDoorClose(QUuid::createUuid().toString(QUuid::WithoutBraces), callback);
}

void JzLift::requestLiftOutLiftCloseDoor(int liftNum, LiftResponseCallback callback)
{
    Q_UNUSED(liftNum);
    requestDoorClose(QUuid::createUuid().toString(QUuid::WithoutBraces), callback);
}

void JzLift::requestDoorOpen(const QString& uuid, LiftResponseCallback callback)
{
    QJsonObject payload;
    payload.insert("cmd", 1);  // 开门
    payload.insert("code", m_liftCode);
    m_dataId = uuid;
    sendPostRequest("/api/v1/elev/doorCmd", payload, callback, &JzLift::onDoorCmdReply);
}

void JzLift::requestDoorClose(const QString& uuid, LiftResponseCallback callback)
{
    QJsonObject payload;
    payload.insert("cmd", 2);  // 关门
    payload.insert("code", m_liftCode);
    m_dataId = uuid;
    sendPostRequest("/api/v1/elev/doorCmd", payload, callback, &JzLift::onDoorCmdReply);
}

QString JzLift::buildUrl(const QString& endpoint) const
{
    if (m_baseUrl.endsWith('/')) {
        return m_baseUrl.left(m_baseUrl.size() - 1) + endpoint;
    }
    return m_baseUrl + endpoint;
}

void JzLift::sendPostRequest(const QString& endpoint,
                             const QJsonObject& payload,
                             LiftResponseCallback callback,
                             void (JzLift::*slot)())
{
    if (!m_networkManager) {
        if (m_logger) {
            m_logger->error("JZLift network manager not initialized");
        }
        if (callback) {
            callback();
        }
        return;
    }

    m_commandCallback = std::move(callback);
    QUrl url(buildUrl(endpoint));
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");

    QJsonDocument doc(payload);
    QByteArray postData = doc.toJson(QJsonDocument::Compact);

    QNetworkReply* reply = m_networkManager->post(request, postData);
    connect(reply, &QNetworkReply::finished, this, slot);

    if (m_logger) {
        m_logger->debug(QString("JZLift %1 POST %2 payload=%3")
            .arg(m_liftNum)
            .arg(url.toString())
            .arg(QString::fromUtf8(postData)));
    }
}

void JzLift::handleHttpFailure(QNetworkReply* reply,
                               const QString& context,
                               LiftResponseCallback& targetCallback)
{
    const QString errorMsg = reply ? reply->errorString() : QStringLiteral("Unknown network error");
    if (m_logger) {
        m_logger->warning(QString("JZLift %1 %2 failed: %3")
            .arg(m_liftNum)
            .arg(context)
            .arg(errorMsg));
    }
    emit requestError(errorMsg);
    if (targetCallback) {
        auto cb = std::move(targetCallback);
        targetCallback = nullptr;
        cb();
    }
}

void JzLift::onStatusReply()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if (!reply) {
        return;
    }

    if (reply->error() != QNetworkReply::NoError) {
        handleHttpFailure(reply, "status request", m_statusCallback);
        reply->deleteLater();
        return;
    }

    const QByteArray payload = reply->readAll();
    const auto jsonDoc = QJsonDocument::fromJson(payload);
    const QJsonObject root = jsonDoc.object();
    if (root.value("code").toInt(-1) != 0) {
        handleHttpFailure(reply,
                          QStringLiteral("status request (code %1)").arg(root.value("code").toInt()),
                          m_statusCallback);
        return;
    }

    const QJsonObject dataObj = root.value("data").toObject();
    m_currentFloor = dataObj.value("floor").toInt();
    m_currentDoorState = dataObj.value("door_state").toInt();

    emit statusUpdated(m_currentFloor, m_currentDoorState);

    if (m_statusCallback) {
        auto cb = std::move(m_statusCallback);
        m_statusCallback = nullptr;
        cb();
    }
    reply->deleteLater();
}

void JzLift::onCallReply()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if (!reply) {
        return;
    }

    if (reply->error() != QNetworkReply::NoError) {
        handleHttpFailure(reply, "call request", m_commandCallback);
        reply->deleteLater();
        return;
    }

    const QJsonObject root = QJsonDocument::fromJson(reply->readAll()).object();
    if (root.value("code").toInt(-1) != 0) {
        handleHttpFailure(reply,
                          QStringLiteral("call request (code %1)").arg(root.value("code").toInt()),
                          m_commandCallback);
        reply->deleteLater();
        return;
    }

    if (m_commandCallback) {
        auto cb = std::move(m_commandCallback);
        m_commandCallback = nullptr;
        cb();
    }
    reply->deleteLater();
}

void JzLift::onDoorCmdReply()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if (!reply) {
        return;
    }

    if (reply->error() != QNetworkReply::NoError) {
        handleHttpFailure(reply, "door command", m_commandCallback);
        reply->deleteLater();
        return;
    }

    const QJsonObject root = QJsonDocument::fromJson(reply->readAll()).object();
    if (root.value("code").toInt(-1) != 0) {
        handleHttpFailure(reply,
                          QStringLiteral("door command (code %1)").arg(root.value("code").toInt()),
                          m_commandCallback);
        reply->deleteLater();
        return;
    }

    if (m_commandCallback) {
        auto cb = std::move(m_commandCallback);
        m_commandCallback = nullptr;
        cb();
    }
    reply->deleteLater();
}

} // namespace lift
} // namespace application

#include "jz_lift.moc"
